#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>
#include<strings.h>
#include<errno.h>
#include<poll.h>
#include <iostream>
#include <GeeJoan/NetWorkPackage.pb.h>
#include <GeeJoan/AuthModule.pb.h>
#include <GeeJoan/IoTModule.pb.h>
#include <chrono>
#include <Business/internaltypes.h>

#define IPADDRESS "127.0.0.1"
#define PORT 12345
#define MAXLINE 1024
#define max(a, b) (a>b) ? a : b


static void handle_connection(int sockfd);

using namespace GeeJoan::Protocol;

int64_t getTimeMicro()
{
    std::chrono::time_point<std::chrono::system_clock> p = std::chrono::system_clock::now();
    return std::chrono::duration_cast<std::chrono::microseconds>(p.time_since_epoch()).count();
}

int main(int argc, char *argv[])
{
    int connfd = 0;
    int clen = 0;
    struct sockaddr_in client;
    if (argc < 2)
    {
        printf("Uasge: clientent[server IP address]\n");
        return -1;
    }

    client.sin_family = AF_INET;
    client.sin_port = htons(PORT);
    client.sin_addr.s_addr = inet_addr(argv[1]);
    connfd = socket(AF_INET, SOCK_STREAM, 0);
    if (connfd < 0)
    {
        perror("socket");
        return -1;
    }

    if (connect(connfd, (struct sockaddr *) &client, sizeof(client)) < 0)
    {
        perror("connect");
        return -1;
    }

    handle_connection(connfd);

    return 0;
}

std::string username;
std::string token;
int64_t taskid_ = 1;
//std::string password;
/**
 * @brief 菜单功能表
 * @param outSendMessage , 需要发送的proto 序列化后的数据
 * @return 0 -- 可以发送  1 --- 忽略本次发送
 */
int MenuFunction(uint8_t *buff, int32_t &length)
{
    // TODO 发送事件表
    std::string tempnetworkpack;
    // 请求包
    NetWork::NetWorkPackageRequest netWorkPackageRequest;
    // TODO 发送事件表
    std::cout << "Menu Type:" << std::endl
              << "1. register " << std::endl
              << "2. login " << std::endl
              << "3. logout " << std::endl
              << "4. downloadKeys " << std::endl
              << "5. change " << std::endl
              << "6. findData " << std::endl
              << "7. LoginDevice " << std::endl
              << "8. Hare " << std::endl
              << "9. Control " << std::endl
              << "10. ControlListerner " << std::endl
              << "11. findGateway " << std::endl
              << "12. CreatDevice " << std::endl
              ;
    int type;
    std::cin >> type;

    switch (type)
    {
        case 1:
        {
            Auth::RegisterAPI registerApi;
            std::string email, password, confirmPassword;
            std::cout << "doing register ... " << std::endl;

            while (email.empty())
            {
                std::cout << "your email : " << email << std::endl;
                std::cin >> email;
            }
            while (username.empty())
            {
                std::cout << "your username : " << username << std::endl;
                std::cin >> username;
            }
            while (password.empty())
            {
                std::cout << "your password : " << password << std::endl;
                std::cin >> password;
            }


            while (confirmPassword != password)
            {
                std::cout << " input your password(confirm) :";
                std::cin >> confirmPassword;
            }

            std::cout << " do call server ..." << std::endl;
            registerApi.set_username(username);
            registerApi.set_email(email);
            registerApi.set_password(password);
            netWorkPackageRequest.set_type_m(NetWork::NetWorkPackageModel::AuthMod);
            netWorkPackageRequest.set_type_sub(1);
            netWorkPackageRequest.set_taskid(taskid_++);
            netWorkPackageRequest.set_body(registerApi.SerializeAsString());
            netWorkPackageRequest.SerializePartialToString(&tempnetworkpack);
            break;
        }

        case 2:
        {
            std::cout << " doing login ..." << std::endl;
            std::string password;

            username.clear();
            while (username.empty())
            {
                std::cout << "your username : " << username << std::endl;
                std::cin >> username;
            }
            while (password.empty())
            {
                std::cout << "your password : " << password << std::endl;
                std::cin >> password;
            }

            int64_t currtime = getTimeMicro();
            std::cout << " do call server ..." << std::endl;

            Auth::LoginRequest loginRequest;
            loginRequest.set_username(username);
            loginRequest.set_password(password);
            loginRequest.set_timestamp(currtime);

            netWorkPackageRequest.set_type_m(NetWork::NetWorkPackageModel::AuthMod);
            netWorkPackageRequest.set_type_sub(2);
            netWorkPackageRequest.set_taskid(taskid_++);
            netWorkPackageRequest.set_body(loginRequest.SerializeAsString());
            netWorkPackageRequest.SerializePartialToString(&tempnetworkpack);
            break;
        }
        case 3 :
        {
            std::cout << " doing logout ..." << std::endl;

            Auth::LogoutAPI logoutApi;
            if (token.empty())
            {
                std::cout << " no login ..." << std::endl;
                return 1;

            }
            logoutApi.set_token(token);
            logoutApi.set_timestamp(getTimeMicro());

            netWorkPackageRequest.set_type_m(NetWork::NetWorkPackageModel::AuthMod);
            netWorkPackageRequest.set_type_sub(3);
            netWorkPackageRequest.set_taskid(taskid_++);
            netWorkPackageRequest.set_body(logoutApi.SerializeAsString());
            netWorkPackageRequest.SerializePartialToString(&tempnetworkpack);
        }
        case 5 :
        {
            std::cout << " doing change Password ..." << std::endl;
            std::string password;
            while (password.empty())
            {
                std::cout << "your password : " << password << std::endl;
                std::cin >> password;
            }
            Auth::ChangePasswordRequest changePasswordRequest;
            changePasswordRequest.set_oldtoken(token);
            changePasswordRequest.set_newpassword(password);
            changePasswordRequest.set_timestamp(getTimeMicro());


            netWorkPackageRequest.set_type_m(NetWork::NetWorkPackageModel::AuthMod);
            netWorkPackageRequest.set_type_sub(5);
            netWorkPackageRequest.set_taskid(taskid_++);
            netWorkPackageRequest.set_body(changePasswordRequest.SerializeAsString());
            netWorkPackageRequest.SerializePartialToString(&tempnetworkpack);
            break;
        }
        case 6:
        {
            //
//            string token = 1; // 用户 token
//            uint64 GatewayId = 2; // 网关id
//            uint64 DevideId = 3; // 终端id
//            int32 key = 4; // 键
//            int64 time = 5; // 时间
//            FindDirectionEnum direction = 6; //放向
//            int32 size = 7; // 数量

            uint64_t gid = 0, did = 0;
            int32_t key = 0, size = 1, direction = 0;
            int64_t currentTime = getTimeMicro();
            std::cout << " findData currtime(" << currentTime << ") " << std::endl;
            while (gid == 0)
            {
                std::cout << " input GatewayId : ";
                std::cin >> gid;
            }

            while (did == 0)
            {
                std::cout << " input DevideId : ";
                std::cin >> did;
            }

            std::cout << " input key(default 0 ) : ";
            std::cin >> key;

            if (key == 0)
            {
                while (!(direction == 1 || direction == 2) || direction == 0)
                {
                    std::cout << " input direction( 1 or 2 ) : ";
                    std::cin >> direction;
                }
                std::cout << " input size(default 1 ) : ";
                std::cin >> size;


                std::cout << " input time default is current:";
                std::cin >> currentTime;

            }

            IoT::FindSensorDataRequest findSensorDataRequest;

            findSensorDataRequest.set_token(token);
            findSensorDataRequest.set_gatewayid(gid);
            findSensorDataRequest.set_deviceid(did);
            findSensorDataRequest.set_key(key);

            findSensorDataRequest.set_direction((IoT::FindDirectionEnum) direction);
            findSensorDataRequest.set_size(size);

            findSensorDataRequest.set_time(currentTime);


            netWorkPackageRequest.set_type_m(NetWork::NetWorkPackageModel::IOTMod);
            netWorkPackageRequest.set_type_sub(2);
            netWorkPackageRequest.set_taskid(taskid_++);
            netWorkPackageRequest.set_body(findSensorDataRequest.SerializeAsString());
            netWorkPackageRequest.SerializePartialToString(&tempnetworkpack);

            break;
        }

        case 7:
        {
            //
            std::string inputToken;

            while (inputToken.empty())
            {
                std::cout << GREEN << " input Token : ";

                std::cin >> inputToken;
            }
            std::cout << RESET << " your Token = " << inputToken << std::endl;
            IoT::LoginDeviceAPI loginDeviceApi;
            loginDeviceApi.set_token(inputToken);
            loginDeviceApi.set_time(getTimeMicro());


            netWorkPackageRequest.set_type_m(NetWork::NetWorkPackageModel::IOTMod);
            netWorkPackageRequest.set_type_sub(IOT_LOGIN_DEVICE);
            netWorkPackageRequest.set_taskid(taskid_++);
            netWorkPackageRequest.set_body(loginDeviceApi.SerializeAsString());
            netWorkPackageRequest.SerializePartialToString(&tempnetworkpack);
            break;
        }
        case 8:
        {
            return -1;
        }

        case 9:
        {

            std::string controlToken, controlData;

            while (controlToken.empty())
            {
                std::cout << GREEN << " input Token : ";
                std::cin >> controlToken;
            }

            while (controlData.empty())
            {
                std::cout << GREEN << " input Data : ";
                std::cin >> controlData;
            }
            std::cout << RESET << " your Token = " << controlToken << " data = " << controlData << std::endl;

            IoT::ControlDeviceRequest controlDeviceRequest;

            controlDeviceRequest.set_token(controlToken);

            controlDeviceRequest.set_data(controlData);

            netWorkPackageRequest.set_type_m(NetWork::NetWorkPackageModel::IOTMod);
            netWorkPackageRequest.set_type_sub(IOT_CONTROL_DEVICE);
            netWorkPackageRequest.set_taskid(taskid_++);
            netWorkPackageRequest.set_body(controlDeviceRequest.SerializeAsString());
            netWorkPackageRequest.SerializePartialToString(&tempnetworkpack);
            break;
        }

        case 10:
        {
            std::string taskCode, errmsg;

            int64_t errCode = 0;

            while (taskCode.empty())
            {
                std::cout << GREEN << " input TaskCode : ";
                std::cin >> taskCode;
            }

            while (errmsg.empty())
            {
                std::cout << GREEN << " input errmsg : ";
                std::cin >> errmsg;
            }

            while (errCode <= 0)
            {
                std::cout << GREEN << " input errCode : ";
                std::cin >> errCode;
            }

            IoT::ControlDeviceAckAPI controlDeviceAckApi;
            controlDeviceAckApi.set_taskcode(taskCode);
            controlDeviceAckApi.set_errcode(errCode);
            controlDeviceAckApi.set_errormsg(errmsg);

            netWorkPackageRequest.set_type_m(NetWork::NetWorkPackageModel::IOTMod);
            netWorkPackageRequest.set_type_sub(IOT_CONTROL_LISTERNER);
            netWorkPackageRequest.set_taskid(taskid_++);
            netWorkPackageRequest.set_body(controlDeviceAckApi.SerializeAsString());
            netWorkPackageRequest.SerializePartialToString(&tempnetworkpack);

            break;
        }

        case 11:
        {
            std::string myToken;

            std::cout << GREEN << " input YourToken( null ) : ";
            std::cin >> myToken;
            std::cout << RESET << " you Token = " << myToken << std::endl;

            IoT::FindGatewayRequest findGatewayRequest;

            findGatewayRequest.set_token(myToken);


            netWorkPackageRequest.set_type_m(NetWork::NetWorkPackageModel::IOTMod);
            netWorkPackageRequest.set_type_sub(IOT_FIND_GATEWAY);
            netWorkPackageRequest.set_taskid(taskid_++);
            netWorkPackageRequest.set_body(findGatewayRequest.SerializeAsString());
            netWorkPackageRequest.SerializePartialToString(&tempnetworkpack);
            break;
        }

        case 12 :{

        }
        default:
        {
            return -1;
        }
    }

    // 结束
    memcpy(buff, tempnetworkpack.c_str(), tempnetworkpack.length());
    length = tempnetworkpack.length();
    return 0;

}

static void handle_connection(int sockfd)
{
    char sendline[MAXLINE], recvline[MAXLINE];
    uint8_t sendProtocolPackageBuff[MAXLINE];
    int maxfdp, stdineof;
    struct pollfd pfds[2];
    int n;
    pfds[0].fd = sockfd;
    pfds[0].events = POLLIN;
    pfds[1].fd = STDIN_FILENO;
    pfds[1].events = POLLIN;

    while (1)
    {
        //nfds：用来指定第一个参数数组元素个数: 2
        poll(pfds, 2, -1);
        if (pfds[0].revents & POLLIN)
        {
            n = read(sockfd, recvline, MAXLINE);
            if (n == 0)
            {
                fprintf(stderr, "client: server is closed.\n");
                close(sockfd);
            }
            // printf("recvline: %s", recvline);

            NetWork::NetWorkPackageRespose networkPackageResp;
            if (!networkPackageResp.ParseFromArray(recvline, n))
            {
                // 不支持
                write(STDOUT_FILENO, "error\r\n", 7);

            } else
            {
                //支持的类型
//                fprintf(stderr , " code : %d  ", (int)networkPackageResp.code());
                std::cout << " code = " << networkPackageResp.code() << " body = " << networkPackageResp.body()
                          << std::endl;
            }
            std::cout << " taskid resp = " << networkPackageResp.taskid() << std::endl;
            if (networkPackageResp.type_m() == NetWork::NetWorkPackageModel::AuthMod)
            {
                switch (networkPackageResp.type_sub())
                {
                    case 1:
                    {
                        std::cout << " register code = " << networkPackageResp.code() << std::endl;
                        break;
                    }
                    case 2:
                    {
                        std::cout << " login code = " << networkPackageResp.code() << std::endl;

                        Auth::LoginRespose loginRespose;
                        if (loginRespose.ParseFromArray(networkPackageResp.body().c_str(),
                                                        networkPackageResp.body().length()));
                        {
                            // 解包成功
                            token = loginRespose.token();
                            std::cout << " my token = " << token << std::endl;
                        }
                        break;
                    }

                    case 3:
                    {
                        std::cout << " logout code = " << networkPackageResp.code() << std::endl;
                        break;
                    }

                    case 4:
                    {
                        std::cout << " head code = " << networkPackageResp.code() << std::endl;
                        break;
                    }
                    case 5:
                    {
                        std::cout << "change code = " << networkPackageResp.code() << std::endl;
                        Auth::ChangePasswordRespose changePasswordRespose;
                        if (changePasswordRespose.ParseFromArray(networkPackageResp.body().c_str(),
                                                                 networkPackageResp.body().length()))
                        {
                            token = changePasswordRespose.newtoken();
                            std::cout << "new token " << token << std::endl;
                        }
                        break;
                    }
                }
            } else if (networkPackageResp.type_m() == NetWork::NetWorkPackageModel::IOTMod)
            {
                switch (networkPackageResp.type_sub())
                {
                    case 1:
                    {
                        std::cout << " upload code = " << networkPackageResp.code() << std::endl;
                        break;
                    }
                    case 2:
                    {
                        std::cout << " find code = " << networkPackageResp.code() << std::endl;
                        break;
                    }

                    case 3:
                    {
                        std::cout << " control code = " << networkPackageResp.code() << std::endl;
                        break;
                    }

                    case 4:
                    {
                        std::cout << " lister code = " << networkPackageResp.code() << std::endl;
                        break;
                    }

                }
            } else
            {
                // undefine
            }

            write(STDOUT_FILENO, recvline, n);
        }
        if (pfds[1].revents & POLLIN)
        {
            // 命令行输入事件
            n = read(STDIN_FILENO, sendline, MAXLINE);
            // sendline 命令行输入的原文 , n 读取到的长度

            if (n == 0)
            {
                printf("n==0\n");
                shutdown(sockfd, SHUT_WR);
                continue;
            }
            int32_t length = 0;
            std::string command(sendline, 4); // 读取命令
            if (command == "menu")
            {
                // 进入 功能表
                if (MenuFunction(sendProtocolPackageBuff, length) != 0)
                {
                    // 跳过
                    continue;
                }

                if (length == 0)
                {
                    continue;
                }
            }

            fprintf(stderr, " len  %d ", length);
            for (int i = 0; i < length; ++i)
            {
                fprintf(stderr, "%02X", sendProtocolPackageBuff[i]);
            }
            fprintf(stderr, "\n");
            write(sockfd, sendProtocolPackageBuff, length);
        }
    }
}
